package com.yjzx.util.common.util.file;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @author guozhi
 * @date 2024/9/11
 * @description LocalClassLoadClasses
 */
public class ClassLoadClassesLocal implements ClassLoadClasses{
    public final static ClassLoadClassesLocal LOCAL_LOAD_CLASSES = new ClassLoadClassesLocal();
    private ClassLoadClassesLocal(){}

    @Override
    public List<Class<?>> findClassesInPackage(String packageName) {
        return findClassesInPackage(packageName, Object.class);
    }
    @Override
    public <T>List<Class<? extends T>> findClassesInPackage(String packageName, Class<T> clazz) {
        List<Class<? extends T>> classes = Collections.emptyList();
        String path = packageName.replace('.', '/');
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        try {
            Enumeration<URL> resources = classLoader.getResources(path);
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                if ("file".equals(resource.getProtocol())) {
                    // 处理文件系统路径
                    File directory = new File(URLDecoder.decode(resource.getFile(), "UTF-8"));
                    classes = loadClassesInDirectory(directory, packageName,clazz);
                } else if ("jar".equals(resource.getProtocol())) {
                    // 处理 JAR 文件
                    String jarPath = resource.getPath().substring(5, resource.getPath().indexOf("!"));
                    JarFile jarFile = new JarFile(URLDecoder.decode(jarPath, "UTF-8"));
                    classes = loadClassesInJar(jarFile, packageName,clazz);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }

    @Override
    public <T> List<Class<? extends T>> loadClassesInDirectory(File directory, String packageName, Class<T> aClass) {
        List<Class<? extends T>> classes = new ArrayList<>();
        if (directory.isDirectory()&&directory.getPath().contains(packageName.replace(ClassLoadClasses.SPOT, File.separator))) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        return loadClassesInDirectory(file, packageName, aClass);
                    } else if (file.getName().endsWith(".class")) {
                        String parent = file.getParent().replace(File.separator,ClassLoadClasses.SPOT);
                        packageName = parent.substring(parent.indexOf(packageName));
                        String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
                        try {
                            Class<?> clazz = Class.forName(className);
                            if (aClass==null||aClass.isAssignableFrom(clazz)){
                                classes.add((Class<? extends T>)clazz);
                            }
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return classes;
    }


    @Override
    public <T>List<Class<? extends T>> loadClassesInJar(JarFile jarFile, String packageName, Class<T> aClass) {
        List<Class<? extends T>> classes = new ArrayList<>();
        Enumeration<JarEntry> entries = jarFile.entries();
        String packagePath = packageName.replace(".", "/");
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            String name = entry.getName();
            if (name.contains(packagePath) && name.endsWith(".class")) {
                String className = name.substring(0, name.length() - 6).replace('/', '.');
                try {
                    Class<?> clazz = Class.forName(className);
                    if (aClass==null||aClass.isAssignableFrom(clazz)){
                        classes.add((Class<? extends T>)clazz);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
        return classes;
    }
}
